#include "mpnet.h"
#include "mpconfig.h"
#include "mpnetclient.h"
#include <math.h>

bool CMpNet::s_bReloadConfig = false;
bool CMpNet::s_bExit = false;

CMpNet::CMpNet():
    m_stServerSocket(PF_LOCAL, SOCK_STREAM, false),
    m_pszClientMemory(NULL)
{
    // 统计项描述
    const char g_aszMpStatDesc[][CTStatItem::CTLIB_DESC_MAX] = 
    {
        "RecvPackage",      //从工作进程接收消息
        "SendPackage",      //向工作进程发送消息
        "RecvMessage",      //从前端接收消息
        "SendMessage",      //向前端发送消息
        "Accept",           //accecpt工作进程的连接
        "NoIdleClient",     //没有空闲工作进程
        "Reconnect",
        "IdleClose",        //达到最大处理条数，工作进程退出的次数
        "MainLoop",         //主循环
        "WaitTimeout",      //waitevent超时
        "Check"
    };

    m_stStat.Init(CT_COUNTOF(g_aszMpStatDesc), g_aszMpStatDesc);
}

CMpNet::~CMpNet()
{
    if (m_pszClientMemory)
    {
        delete [] m_pszClientMemory;
        m_pszClientMemory = NULL;
    }
}

void CMpNet::DumpStatInfo()
{
    m_stStat.Dump(CT_STAT);
}

void CMpNet::Init()
{
    int iRet = 0;

    //安装信号处理函数
    signal(SIGUSR1, CMpNet::sigusr1_handle);	// kill -USR1 pid 会让程序重读配置
    signal(SIGUSR2, CMpNet::sigusr2_handle);	// kill -USR2 pid 会让程序安全的退出

    // 创建Epoll
    iRet = m_stEpoll.Init(EPOLL_SIZE, EPOLL_WAIT_SIZE);
    CHECK_THROW(iRet);

    // 初始化ClientFactory
    const int iObjectCount = CONF->ProcessNum * 2;
    const size_t iCountSize = CMpNetClientFactory::CountSize(iObjectCount);
    m_pszClientMemory = new char[iCountSize];
    if (m_pszClientMemory == NULL)
    {
        CT_THROW(());
    }

    iRet = m_stClientFactory.Init(m_pszClientMemory, iCountSize, iObjectCount);
    CHECK_THROW(iRet);

    // 创建共享内存队列
    iRet = m_stShmQueue.Init(true, 
            CONF->FrontEndShmKey, CONF->FrontEndShmSize,
            CONF->FrontEndSocket, CONF->BackEndSocket,
            m_stEpoll,
            *this,
            &CMpNet::OnShmQueueEvent);
    CHECK_THROW(iRet);

    // 创建与工作进程通讯的Socket
    iRet = m_stServerSocket.Listen(CONF->ProcessSocket);
    CHECK_THROW(iRet);

    // 把m_stServerSocket加入到Epoll
    iRet = m_stServerObject.Register(
            *this,
            &CMpNet::OnServerEvent,
            m_stEpoll,
            m_stServerSocket.GetSocket(),
            EPOLLIN);
    CHECK_THROW(iRet);

    CT_INFO(("mpsvr net process init ok"));
}

void CMpNet::Run()
{
    int iRet = 0;
    int iLoop = 0;
    int iPrevNow = 0;

    while (true)
    {
        m_stStat.Put(mp_stat_mainloop);

        // 信号检查
        if (s_bReloadConfig)
        {
            s_bReloadConfig = false;
            ReloadConfig();
        }

        if (s_bExit)
        {
            SendRemainData();
            s_bExit = false;
            return;
        }

        if (++iLoop >= 100)
        {
            iLoop = 0;
            int iNow = time(NULL);

            if (iNow - iPrevNow >= 30)
            {
                iPrevNow = iNow;

                DumpStatInfo();
            }
        }

        iRet = m_stEpoll.WaitAndEvent(10);
        if (iRet == 0)
        {
            m_stStat.Put(mp_stat_waittimeout);
        }
        HandleShmQueueMsg();

        //TODO: 检查Client是否有需要退出的
    }
}

void CMpNet::Exit()
{
    CT_INFO(("mpnet exit"));
}

void CMpNet::sigusr1_handle(int CT_UNUSED(iSigVal))
{
    s_bReloadConfig = true;
    signal(SIGUSR1, CMpNet::sigusr1_handle);
}

void CMpNet::sigusr2_handle(int CT_UNUSED(iSigVal))
{
    s_bExit = true;
    signal(SIGUSR2, CMpNet::sigusr2_handle);
}

void CMpNet::ReloadConfig()
{
}

void CMpNet::OnServerEvent(CMpNetEpollObject* pstObject, SOCKET iSocket, int iEvent)
{
    int iRet = 0;

    if (CTEpoll::IsCloseEvent(iEvent)) // 关闭连接事件
    {
        CT_WARNING(("server(%p, %d) recv close event(%d), errno=%d",
            pstObject, iSocket, iEvent, CT_ERRNO));
    }
    else if (CTEpoll::IsInputEvent(iEvent))   // 有新连接
    {
        CT_TRACE(("has new connect"));
        SOCKET iNewSoket = INVALID_SOCKET;
        iRet = m_stServerSocket.Accept(iNewSoket);
        if (iRet)
        {
            CT_WARNING(("m_stServerSocket.Accept failed %d", iRet));
            return;
        }

        m_stStat.Put(mp_stat_accept);

        CMpNetClient* pClient = m_stClientFactory.New();
        if (!pClient)
        {
            CT_WARNING(("m_stClientFactory.New failed"));
            return;
        }

        pClient->Init(*this, iNewSoket, GetMaxRunCount());
        CT_TRACE(("create new client ok. socket=%d", iNewSoket));
    }
}

void CMpNet::OnClientEvent(CMpNetEpollObject* pstObject, SOCKET iSocket, int iEvent)
{
    int iRet = 0;
    CMpNetClient* pstClient = (CMpNetClient*) pstObject;

    CT_TRACE(("client(%p, %d) has event %d", pstClient, iSocket, iEvent));

    if (CTEpoll::IsCloseEvent(iEvent))
    {
        // 客户端退出了 清理
        CT_TRACE(("recv close event"));    
        OnClientClose(pstClient);
    }
    else if (CTEpoll::IsInputEvent(iEvent)) // 接收数据
    {
        char* pszCode = NULL;
        int iCodeLen = 0;
        while (true)
        {
            pstClient->RecvOneCode(pszCode, iCodeLen);
            CT_TRACE(("client(%p, %d) recv one code, len=%d msg=%s",
                pstClient, iSocket, iCodeLen, pszCode));

            if (iCodeLen > 0)
            {
                // 取出一个Code 放到共享内存队列
                iRet = m_stShmQueue.PutOneCode(pszCode, iCodeLen);
                if (iRet)
                {
                    CT_WARNING(("m_stShmQueue.PutOneCode(%p, %d) failed %d",
                        pszCode, iCodeLen, iRet));
                }
                pstClient->RemoveOneCode(iCodeLen);

                m_stStat.Put(mp_stat_sendmsg);

                CT_TRACE(("client(%p, %d) PutOneCode(%p, %d) ok",
                    pstClient, iSocket, pszCode, iCodeLen));

                // 如果没有数据要发送了，把该Client设置为空闲状态
                CT_TRACE(("client(%p, %d) now idle", pstClient, iSocket));
                iRet = m_stClientFactory.SetIdle(pstClient);
                CT_TRACE(("SetIdle return %d", iRet));

                if (pstClient->IsReachMaxRunCount())
                {
                    // 这里发送子进程退出命令
                    // 当子进程退出后 会收到事件 那时再清理
                    CT_TRACE(("ReachMaxRunCount"));
                    m_stStat.Put(mp_stat_idleclose);
                    NotifyClientClose(pstClient);
                }
                break;
            }
            else if (iCodeLen <= 0)
            {
                CT_WARNING(("client(%p, %d) recv one invalid code, len=%d",
                    pstClient, iSocket, iCodeLen));
				break;
            }
        }
    }
    else if (CTEpoll::IsOutputEvent(iEvent))
    {
        CT_WARNING(("client(%p, %d) recv close event(%d), errno=%d",
            pstClient, iSocket, iEvent, CT_ERRNO));
    }
}

void CMpNet::OnShmQueueEvent(CMpNetEpollObject* pstObject, SOCKET iSocket, int iEvent)
{
    //thinkryzhu 这里会导致不停的收到事件, 死循环
//    if (m_stClientFactory.GetIdleClientCount() <= 0)  //如果没有空闲的client则等待
//    {
//        return;
//    }

    CT_TRACE(("OnShmQueueEvent socket=%d event=%d", iSocket, iEvent));

    m_stShmQueue.ClearNotify(); // 接收Notify数据, 否则会一直收到事件

    if (CTEpoll::IsInputEvent(iEvent))
    {
        HandleShmQueueMsg();
    }
}

void CMpNet::HandleShmQueueMsg()
{
    for (;;)
    {
        if (!m_stShmQueue.HasCode())
        {
            return;
        }

        if (m_stClientFactory.GetUsedClientCount() == 0)
        {
            // 此时mphandle还没fork, 直接return
            return;
        }

        if (m_stClientFactory.GetIdleClientCount() <= 0)  //没有空闲的client则等待
        {
            m_stStat.Put(mp_stat_no_idle_client);
            return;
        }

        // 寻找空闲的client来处理
        CMpNetClient* pstClient = m_stClientFactory.AllocOneIdle();
        if (pstClient == NULL)
        {
            CT_WARNING(("not find idle client, return"));
            return;
        }
        
        // 获取Code
        char* pszBuffer = NULL;
        int iBufferLen = 0;
        pstClient->GetSendBuffer(pszBuffer, iBufferLen);
        CT_TRACE(("OnShmQueueEvent client's send buffer len=%d", iBufferLen));

        int iCodeLen = 0;
        int iRet = m_stShmQueue.GetOneCode(pszBuffer, iBufferLen, iCodeLen);
        if (iRet)
        {
            m_stClientFactory.SetIdle(pstClient);
            CT_WARNING(("m_stShmQueue.GetOneCode failed %d", iRet));
            return;
        }
        else if (iCodeLen == 0 || iCodeLen == sizeof(CEnpNetHead)) // 没有消息要处理了
        {
            m_stClientFactory.SetIdle(pstClient);
            return;
        }

        m_stStat.Put(mp_stat_recvmsg);
        CT_TRACE(("OnShmQueueEvent GetOneCode len=%d", iCodeLen));
        pstClient->SendOneCode(iCodeLen);
    }
}

void CMpNet::SendRemainData()
{
    // 完整的逻辑应该是:
    // 不再从共享内存队列读取消息
    // 等待所有Busy的Client发送完数据/或退出后才退出
    // 缓冲区中可能还有未发送完的数据
}

void CMpNet::NotifyClientClose(CMpNetClient* pstClient)
{
    // 发送关闭命令
    // 发送一个长度为0的消息就是要求退出
    pstClient->SendOneCode(0);
    CT_TRACE(("SendOneCode(0) to client over"));

    // 并把节点状态设为正在退出
    m_stClientFactory.SetClosing(pstClient);
    CT_TRACE(("SetClosing over"));
}

void CMpNet::OnClientClose(CMpNetClient* pstClient)
{
    int iRet = 0;

    // 如果不是closing状态就是异常
    if (!m_stClientFactory.IsClosing(pstClient))
    {
        CT_WARNING(("client(%p)' status(%d) is not closing",
            pstClient, m_stClientFactory.GetStatus(pstClient)));
    }

    // 从ClientFacotry中释放
    iRet = m_stClientFactory.Delete(pstClient);
    if (iRet)
    {
        CT_WARNING(("delete client(%p) failed %d", pstClient, iRet));
    }
    else
    {
        CT_TRACE(("delete client(%p) ok", pstClient));
    }
}

int CMpNet::GetMaxRunCount()
{
    static const float MAX_PERCENT = 1.5;
    static const int MAX_COUNT = (int)floor(CONF->ProcessLoopNum * MAX_PERCENT) & 0x7FFFFFFF;
    static const int INTERVAL_COUNT = abs(MAX_COUNT - CONF->ProcessLoopNum) / CONF->ProcessNum;

    static int iNowLoopCount;
    iNowLoopCount = ( (iNowLoopCount >= MAX_COUNT  || iNowLoopCount == 0) ? CONF->ProcessLoopNum : iNowLoopCount + INTERVAL_COUNT);
    CT_TRACE(("now loop count = %d", iNowLoopCount ));
    return iNowLoopCount;
}

